# -*- coding: utf-8 -*-

import json
import logging
import re
import jieba
from typing import Dict, Any, List, Optional
from datetime import datetime

# 确保日志配置正确
if not logging.getLogger().handlers:
    logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')

# 使用一个固定的logger名称，确保全局一致性
logger = logging.getLogger("patient_state_builder")
logger.setLevel(logging.INFO)  # 显式设置级别

# 确保logger有handler
if not logger.handlers:
    # 添加一个stream handler，直接输出到控制台
    handler = logging.StreamHandler()
    formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
    handler.setFormatter(formatter)
    handler.setLevel(logging.INFO)
    logger.addHandler(handler)
    logger.propagate = True  # 允许传播到父logger


class PatientStateBuilder:
    """
    患者状态构建模块
    
    功能：将输入的患者基础信息、门诊病历、历史对话转化为结构化可检索的状态表示
    实现模块一：从患者对话中提取结构化信息
    """
    
    def __init__(self, config=None, model_handler=None):
        """
        初始化患者状态构建器
        
        Args:
            config: 配置对象
            model_handler: 模型处理器，用于LLM调用
        """
        self.config = config
        self.model_handler = model_handler
        self.current_time = datetime.now().isoformat()
        # 医学词典，用于规则抽取
        self.medical_dict = {
            '疼痛部位': ['肚子', '腹部', '胸部', '头部', '额头', '太阳穴', '腰部', '背部', '胃部', '心口', '肩膀', '手臂', '腿部', '膝盖', '头部两侧', '头顶部', '后脑勺'],
            '疼痛性质': ['疼', '痛', '难受', '不舒服', '胀痛', '刺痛', '隐痛', '剧烈', '持续', '间歇', '呲啦呲啦', '绞痛', '隐隐作痛'],
            '症状描述': ['发热', '咳嗽', '恶心', '呕吐', '腹泻', '便秘', '头晕', '乏力', '胸闷', '气短', '心悸', '睡眠不好', '失眠'],
            '时间描述': ['昨天', '今天', '前天', '上周', '最近', '刚才', '早上', '晚上', '中午', '夜里', '凌晨', '一周', '三天'],
            '程度描述': ['很', '非常', '有点', '轻微', '严重', '厉害', '不太', '稍微', '特别']
        }
        # 初始化jieba分词器
        self._init_jieba()
        
    def _init_jieba(self):
        """
        初始化jieba分词器，加载医学词典
        """
        try:
            # 将医学词典中的词汇添加到jieba
            for category, words in self.medical_dict.items():
                for word in words:
                    jieba.add_word(word)
        except Exception as e:
            logger.warning(f"初始化jieba分词器失败: {str(e)}")
    
    # build_patient_state方法在文件后面有更完整的实现，这里不再重复定义
    
    def _parse_basic_info(self, basic_info: Any) -> Dict[str, Dict[str, Any]]:
        """
        解析患者基本信息
        
        Args:
            basic_info: 基础信息（可能是字符串、字典、JSON字符串或None）
            
        Returns:
            结构化的基本信息
        """
        result = {}
        
        try:
            # 尝试解析JSON字符串
            if isinstance(basic_info, str):
                try:
                    import json
                    parsed_dict = json.loads(basic_info)
                    basic_info = parsed_dict
                except:
                    # 如果不是有效的JSON，继续按照普通字符串处理
                    pass
            
            if isinstance(basic_info, dict):
                for key, value in basic_info.items():
                    result[key] = {
                        "value": str(value) if value is not None else "未知",
                        "source": "输入",
                        "confidence": 1.0
                    }
            elif isinstance(basic_info, str):
                # 尝试解析字符串格式的基本信息
                for line in basic_info.split('\n'):
                    if ':' in line or '：' in line:
                        key, value = re.split(r'[:：]\s*', line.strip(), maxsplit=1)
                        result[key.strip()] = {
                            "value": value.strip(),
                            "source": "输入",
                            "confidence": 1.0
                        }
            else:
                # 默认值
                result = {
                    "姓名": {"value": "未知", "source": "缺失", "confidence": 0.0},
                    "性别": {"value": "未知", "source": "缺失", "confidence": 0.0},
                    "年龄": {"value": "未知", "source": "缺失", "confidence": 0.0}
                }
        except Exception as e:
            logger.warning(f"解析基本信息失败: {str(e)}")
            result = {
                "姓名": {"value": "未知", "source": "解析失败", "confidence": 0.0},
                "性别": {"value": "未知", "source": "解析失败", "confidence": 0.0},
                "年龄": {"value": "未知", "source": "解析失败", "confidence": 0.0}
            }
        
        return result
    
    def _parse_medical_record(self, medical_record: Any) -> Dict[str, Any]:
        """
        解析门诊病历信息
        
        Args:
            medical_record: 门诊病历（可能是字符串、字典、JSON字符串或None）
            
        Returns:
            结构化的病历信息，确保所有病历字段都是同级关系
        """
        result = {}
        
        try:
            # 尝试解析JSON字符串
            if isinstance(medical_record, str):
                try:
                    import json
                    parsed_dict = json.loads(medical_record)
                    medical_record = parsed_dict
                except:
                    # 如果不是有效的JSON，继续按照普通字符串处理
                    pass
            
            if isinstance(medical_record, dict):
                # 直接处理字典格式的病历，确保所有字段保持同级关系
                # 首先处理所有字段，确保每个字段都被保留
                for key, value in medical_record.items():
                    # 保留原始键名，确保所有字段同级
                    if key == "主诉":
                        # 主诉单独处理，确保纯净，不包含现病史信息
                        # 检查value是否可能是JSON字符串格式
                        if isinstance(value, str) and (value.startswith('{') or ',' in value):
                            try:
                                import json
                                parsed_value = json.loads(value)
                                if isinstance(parsed_value, dict):
                                    # 如果是字典格式，提取其中的value字段或使用整个字典
                                    if 'value' in parsed_value:
                                        result[key] = parsed_value
                                    else:
                                        # 只保留value字段，确保结构一致性
                                        result[key] = {
                                            "value": str(parsed_value),
                                            "source": "门诊病历",
                                            "confidence": 1.0
                                        }
                            except:
                                # 如果解析失败，作为普通字符串处理
                                result[key] = {
                                    "value": str(value) if value is not None else "无",
                                    "source": "门诊病历",
                                    "confidence": 1.0
                                }
                        else:
                            result[key] = {
                                "value": str(value) if value is not None else "无",
                                "source": "门诊病历",
                                "confidence": 1.0
                            }
                    elif key == "现病史":
                        # 现病史单独处理，使用专门的解析方法
                        result[key] = self._parse_present_illness(value)
                    elif key == "体格检查":
                        result[key] = {
                            "阳性体征": {
                                "value": str(value) if value is not None else "无专科阳性特征",
                                "source": "病历",
                                "confidence": 0.9
                            }
                        }
                    elif key == "辅助检查":
                        result[key] = {
                            "结果": {
                                "value": str(value) if value is not None else "无",
                                "source": "病历",
                                "confidence": 1.0
                            }
                        }
                    # 对于其他所有字段，都保持同级关系
                    else:
                        result[key] = {
                            "value": str(value) if value is not None else "无",
                            "source": "门诊病历",
                            "confidence": 1.0
                        }
                        
                # 特殊处理：如果存在"处理"、"注意事项"、"患者去向"这些字段，同时也创建"处理与建议"结构
                # 但不改变原字段的同级关系
                if any(key in result for key in ["处理", "注意事项", "患者去向"]):
                    result["处理与建议"] = {}
                    for key in ["处理", "注意事项", "患者去向"]:
                        if key in result:
                            result["处理与建议"][key] = result[key]
            elif isinstance(medical_record, str):
                # 尝试解析字符串格式的病历
                result = self._parse_string_medical_record(medical_record)
            else:
                # 默认空值
                result = self._get_default_medical_record()
                
        except Exception as e:
            logger.warning(f"解析门诊病历失败: {str(e)}")
            result = self._get_default_medical_record()
        
        return result
    
    def _parse_present_illness(self, present_illness: Any) -> Dict[str, Any]:
        """
        解析现病史信息
        
        Args:
            present_illness: 现病史内容
            
        Returns:
            结构化的现病史
        """
        result = {}
        
        try:
            if isinstance(present_illness, dict):
                # 从字典中提取信息
                result["起病时间"] = {
                    "value": present_illness.get("起病时间", "未知"),
                    "source": "病历",
                    "confidence": 0.9
                }
                result["症状"] = {
                    "value": present_illness.get("症状", "未知"),
                    "source": "病历", 
                    "confidence": 1.0
                }
                result["诱因"] = {
                    "value": present_illness.get("诱因", "未知"),
                    "source": "病历",
                    "confidence": 1.0
                }
                result["伴随症状"] = {
                    "value": present_illness.get("伴随症状", None),
                    "source": "缺失" if present_illness.get("伴随症状") is None else "病历",
                    "confidence": 0.0 if present_illness.get("伴随症状") is None else 1.0
                }
                result["进展"] = {
                    "value": present_illness.get("进展", "未知"),
                    "source": "病历",
                    "confidence": 0.8
                }
            else:
                # 从字符串中提取信息
                illness_text = str(present_illness) if present_illness else ""
                result = self._extract_illness_from_text(illness_text)
        except Exception as e:
            logger.warning(f"解析现病史失败: {str(e)}")
            result = {
                "起病时间": {"value": "未知", "source": "解析失败", "confidence": 0.0},
                "症状": {"value": "未知", "source": "解析失败", "confidence": 0.0},
                "诱因": {"value": "未知", "source": "解析失败", "confidence": 0.0},
                "伴随症状": {"value": None, "source": "缺失", "confidence": 0.0},
                "进展": {"value": "未知", "source": "解析失败", "confidence": 0.0}
            }
        
        return result
    
    def _extract_illness_from_text(self, text: str) -> Dict[str, Any]:
        """
        从文本中提取现病史信息
        
        Args:
            text: 现病史文本
            
        Returns:
            提取的现病史信息
        """
        result = {}
        
        # 简单的关键词匹配提取
        time_patterns = [r'(\d+[天日月年])', r'([一二三四五六七八九十]+[天日月年])', r'(数[天日月年])']
        symptom_patterns = [r'(疼痛|咳嗽|发热|头痛|腹痛|胸痛|恶心|呕吐|腹泻|便秘)']
        
        # 提取起病时间
        time_match = None
        for pattern in time_patterns:
            match = re.search(pattern, text)
            if match:
                time_match = match.group(1)
                break
        
        result["起病时间"] = {
            "value": time_match if time_match else "未知",
            "source": "病历" if time_match else "缺失",
            "confidence": 0.9 if time_match else 0.0
        }
        
        # 提取症状
        symptom_match = None
        for pattern in symptom_patterns:
            match = re.search(pattern, text)
            if match:
                symptom_match = match.group(1)
                break
        
        result["症状"] = {
            "value": symptom_match if symptom_match else "未知",
            "source": "病历" if symptom_match else "缺失",
            "confidence": 1.0 if symptom_match else 0.0
        }
        
        # 其他字段使用默认值
        result["诱因"] = {"value": "未知", "source": "缺失", "confidence": 0.0}
        result["伴随症状"] = {"value": None, "source": "缺失", "confidence": 0.0}
        result["进展"] = {"value": "未知", "source": "缺失", "confidence": 0.0}
        
        return result
    
    def build_patient_state(self, patient_data: Dict[str, Any]) -> Dict[str, Any]:
        """
        构建患者结构化状态
        
        Args:
            patient_data: 原始患者数据，包含基础信息、人格、门诊病历、门诊对话等
            
        Returns:
            结构化的患者状态字典
        """
        try:
            # 解析基础信息
            basic_info = self._parse_basic_info(patient_data.get("基础信息", {}))
            
            # 解析门诊病历
            medical_record = self._parse_medical_record(patient_data.get("门诊病历", {}))
            
            # 构建初始患者状态
            patient_state = {
                "PatientState": {
                    "基本信息": basic_info,
                    **medical_record,
                    "系统状态": {
                        "更新时间": self.current_time,
                        "对话轮次": len(patient_data.get("门诊对话", [])),
                        "信息完整度": 0.0  # 将在后面计算
                    }
                }
            }
            logger.info(f"构建初始患者状态:{patient_state}")
            # 解析历史对话，提取并更新主观表述（同时兼容"门诊对话"和"context_history"键名）
            dialogues = patient_data.get("门诊对话", patient_data.get("context_history", []))
            dialogue_info = self._extract_structured_info_from_dialogue(dialogues, patient_state)
            
            # 更新患者状态
            if "主观表述" in dialogue_info:
                patient_state["PatientState"]["主观表述"] = dialogue_info["主观表述"]
            
            # 计算并更新信息完整度
            patient_state["PatientState"]["系统状态"]["信息完整度"] = \
                self._calculate_completeness(basic_info, medical_record, dialogue_info)
            
            logger.info(f"成功构建患者状态，信息完整度: {patient_state['PatientState']['系统状态']['信息完整度']}")
            return patient_state
            
        except Exception as e:
            logger.error(f"构建患者状态失败: {str(e)}")
            return self._get_empty_state()
    
    def _extract_structured_info_from_dialogue(self, dialogues: List[Dict[str, str]], patient_state: Dict[str, Any]) -> Dict[str, Any]:
        """
        从对话中提取结构化信息并更新患者状态（实现模块一功能）
        
        Args:
            dialogues: 对话列表
            patient_state: 当前患者状态
            
        Returns:
            更新后的对话信息
        """
        result = {"主观表述": {}}
        
        try:
            if not dialogues:
                return result
            
            # Step 1: 预处理阶段
            patient_speeches = self._preprocess_dialogue(dialogues)
            
            if not patient_speeches:
                return result
            
            # Step 2: 规则抽取阶段
            rule_extracted_info = self._rule_based_extraction(patient_speeches)
            logger.info(f"规则抽取阶段:{rule_extracted_info}")
            
            # 获取门诊病历信息（从patient_state中提取）
            medical_record = {}
            if patient_state and "PatientState" in patient_state:
                # 提取所有source为门诊病历的字段
                for key, value in patient_state["PatientState"].items():
                    # 检查字段是否有source属性且为门诊病历
                    if isinstance(value, dict) and value.get("source") == "门诊病历":
                        medical_record[key] = value
                    # 或者字段直接是门诊病历的一部分
                    elif key not in ["基本信息", "系统状态", "主观表述"]:
                        medical_record[key] = value
            
            # Step 3: LLM补充阶段（传入门诊病历信息）
            llm_supplemented_info = self._llm_supplement(patient_speeches, rule_extracted_info, medical_record)
            logger.info(f"LLM补充阶段:{llm_supplemented_info}")
            
            # 合并规则抽取和LLM补充的信息
            extracted_info = {**rule_extracted_info, **llm_supplemented_info}
            # Step 4: 一致性校验阶段
            validated_info = self._consistency_check(extracted_info, patient_state,rule_extracted_info, llm_supplemented_info)
            logger.info(f"一致性校验阶段:{validated_info}")
            # 更新主观表述
            result["主观表述"] = validated_info
            
        except Exception as e:
            logger.warning(f"从对话中提取结构化信息失败: {str(e)}")
        
        return result
    
    def _preprocess_dialogue(self, dialogues: List[Dict[str, str]]) -> List[str]:
        """
        预处理对话历史
        
        Args:
            dialogues: 对话列表
            
        Returns:
            清洗后的患者发言列表
        """
        patient_speeches = []
        
        try:
            for dialogue in dialogues:
                if isinstance(dialogue, dict) and "患者" in dialogue:
                    # 清洗患者发言
                    speech = str(dialogue["患者"])
                    # 移除多余空格、特殊字符
                    speech = re.sub(r'\s+', ' ', speech)
                    speech = re.sub(r'[\r\n]+', ' ', speech)
                    speech = speech.strip()
                    if speech:
                        patient_speeches.append(speech)
        except Exception as e:
            logger.warning(f"预处理对话失败: {str(e)}")
        
        return patient_speeches
    
    def _rule_based_extraction(self, patient_speeches: List[str]) -> Dict[str, Dict[str, Any]]:
        """
        规则抽取阶段：增强疼痛性质提取，移除年龄提取，保留核心医疗维度
        """
        extracted_info = {}
        combined_text = " ".join(patient_speeches).lower()  # 统一小写，提升匹配稳定性

        # --------------------------
        # 1. 疼痛相关信息（核心强化）
        # --------------------------
        # 1.1 疼痛部位（保留原有逻辑，支持同义词）
        pain_locations = []
        location_synonyms = {
            "肚子": ["腹部", "肚", "下腹", "上腹"],
            "胃": ["胃部", "心口窝", "上腹部"],
            "肠": ["肠子", "肠道", "下腹"],
            "肝": ["肝脏", "右上腹"],
            "头": ["头部", "头疼位置"]
        }
        for base_loc, synonyms in location_synonyms.items():
            if base_loc in combined_text or any(syn in combined_text for syn in synonyms):
                pain_locations.append(base_loc)
        for loc in self.medical_dict['疼痛部位']:
            if loc not in location_synonyms and loc in combined_text:
                pain_locations.append(loc)
        
        pain_locations = list(set(pain_locations))
        specific_locations = [loc for loc in pain_locations if loc not in ['头部', '腹部', '胸部']]
        if specific_locations:
            extracted_info["疼痛部位"] = {
                "value": specific_locations[0],
                "source": "对话历史",
                "confidence": 0.9
            }
            if len(specific_locations) > 1:
                extracted_info["疼痛部位详细"] = {
                    "value": "、".join(specific_locations),
                    "source": "对话历史",
                    "confidence": 0.85
                }
        elif pain_locations:
            extracted_info["疼痛部位"] = {
                "value": pain_locations[0],
                "source": "对话历史",
                "confidence": 0.85
            }

        # 1.2 疼痛性质（重点强化：细分类型+程度+特征）
        # 1.2.1 疼痛基础类型（医学标准分类）
        pain_types = {
            "胀痛": ["胀", "胀痛", "胀得慌"],
            "刺痛": ["刺疼", "刺痛", "针扎一样"],
            "隐痛": ["隐隐作痛", "轻微疼", "丝丝拉拉疼"],
            "绞痛": ["绞痛", "拧着疼", "一阵一阵抽着疼"],
            "烧灼痛": ["烧心", "火辣辣", "烧灼样"]
        }
        matched_types = []
        for pain_type, keywords in pain_types.items():
            for kw in keywords:
                if kw in combined_text:
                    matched_types.append(pain_type)
                    break  # 每个类型只匹配一次
        
        # 1.2.2 疼痛程度（结合程度词）
        pain_degrees = {
            "轻微": ["有点", "轻微", "不太", "稍微"],
            "中度": ["挺", "比较", "明显"],
            "重度": ["很", "非常", "剧烈", "厉害", "受不了"]
        }
        matched_degree = None
        for degree, keywords in pain_degrees.items():
            for kw in keywords:
                if re.search(f"{kw}.*?(疼|痛|难受)", combined_text):
                    matched_degree = degree
                    break
            if matched_degree:
                break  # 只取最显著的程度
        
        # 1.2.3 疼痛特征（发作模式）
        pain_features = {
            "持续性": ["一直疼", "持续疼", "不停疼"],
            "间歇性": ["一阵一阵", "偶尔疼", "时好时坏"],
            "阵发性": ["突然疼", "一阵剧痛", "发作时疼"]
        }
        matched_feature = None
        for feature, keywords in pain_features.items():
            for kw in keywords:
                if kw in combined_text:
                    matched_feature = feature
                    break
            if matched_feature:
                break
        
        # 1.2.4 合并疼痛性质描述（完整维度）
        pain_nature_parts = []
        if matched_types:
            pain_nature_parts.append(f"类型：{'; '.join(matched_types)}")
        if matched_degree:
            pain_nature_parts.append(f"程度：{matched_degree}")
        if matched_feature:
            pain_nature_parts.append(f"特征：{matched_feature}")
        
        # 补充原始疼痛词（如果未被覆盖）
        if not pain_nature_parts:
            for nature in self.medical_dict['疼痛性质']:
                if nature in combined_text:
                    pain_nature_parts.append(nature)
        
        if pain_nature_parts:
            extracted_info["疼痛性质"] = {
                "value": "; ".join(pain_nature_parts),
                "source": "对话历史",
                "confidence": 0.9
            }

        # 1.3 疼痛频率及时长
        pain_frequency_patterns = [
            (r'偶尔', '偶尔'),
            (r'有时候', '有时'),
            (r'经常', '频繁'),
            (r'一直', '持续'),
            (r'每天', '每日发作')
        ]
        for pattern, desc in pain_frequency_patterns:
            if re.search(pattern, combined_text):
                extracted_info["疼痛频率"] = {
                    "value": desc,
                    "source": "对话历史",
                    "confidence": 0.85
                }
                break
        
        # --------------------------
        # 2. 其他症状提取
        # --------------------------
        symptoms = []
        extended_symptoms = self.medical_dict['症状描述'] + [
            "反酸", "烧心", "嗳气", "腹胀", "消化不良", 
            "排便异常", "便血", "黑便", "体重下降"
        ]
        for symptom in extended_symptoms:
            if symptom in combined_text:
                关联_pattern = f"[吃饭|进食|空腹|早上|晚上].*?{symptom}"
                match = re.search(关联_pattern, combined_text)
                if match:
                    symptoms.append(match.group(0))
                else:
                    symptoms.append(symptom)
        if symptoms:
            extracted_info["其他症状"] = {
                "value": "; ".join(symptoms),
                "source": "对话历史",
                "confidence": 0.85
            }

        # --------------------------
        # 3. 时间相关信息
        # --------------------------
        onset_patterns = [
            (r'(\d+年|几年)前开始', r'\1前开始'),
            (r'最近(\d+天|几天)', r'最近\1'),
            (r'(昨天|前天)开始', r'\1开始')
        ]
        for pattern, desc in onset_patterns:
            match = re.search(pattern, combined_text)
            if match:
                extracted_info["发病时间描述"] = {
                    "value": match.group(0),
                    "source": "对话历史",
                    "confidence": 0.85
                }
                break
        
        duration_patterns = [
            (r'疼了(\d+分钟|几小时)', r'持续\1'),
            (r'疼一会儿就好', '短暂性，可自行缓解')
        ]
        for pattern, desc in duration_patterns:
            match = re.search(pattern, combined_text)
            if match:
                extracted_info["症状持续时间"] = {
                    "value": match.group(0),
                    "source": "对话历史",
                    "confidence": 0.8
                }
                break

        # --------------------------
        # 4. 饮食/排便相关
        # --------------------------
        diet_patterns = [
            (r'吃饭后(疼|不舒服)', '餐后不适'),
            (r'空腹(疼|难受)', '空腹不适'),
            (r'吃辣|吃油后(不舒服)', '刺激性饮食后不适')
        ]
        for pattern, desc in diet_patterns:
            if re.search(pattern, combined_text):
                extracted_info["饮食相关"] = {
                    "value": desc + f"（证据：{re.search(pattern, combined_text).group(0)}）",
                    "source": "对话历史",
                    "confidence": 0.85
                }
                break
        
        bowel_patterns = [
            (r'大便后(缓解|不疼了)', '排便后缓解'),
            (r'便秘时(加重|更疼)', '便秘时加重'),
            (r'拉肚子后(不舒服)', '腹泻后不适')
        ]
        for pattern, desc in bowel_patterns:
            if re.search(pattern, combined_text):
                extracted_info["排便相关"] = {
                    "value": desc + f"（证据：{re.search(pattern, combined_text).group(0)}）",
                    "source": "对话历史",
                    "confidence": 0.85
                }
                break

        # --------------------------
        # 5. 独立维度：检查史、治疗偏好、情绪状态
        # --------------------------
        # 检查史
        checkup_patterns = [
            (r'(\d+年|几年)前做过(胃镜|肠镜|肠胃镜)', r'\1前做过\2'),
            (r'上次做(胃镜|肠镜)是(什么时候|几年前)', '有既往内镜检查史，具体时间未明确'),
            (r'取了个样本|活检', '曾做过病理活检')
        ]
        for pattern, desc in checkup_patterns:
            match = re.search(pattern, combined_text)
            if match:
                extracted_info["检查史"] = {
                    "value": match.group(0) if match.group(0) else desc,
                    "source": "对话历史",
                    "confidence": 0.9
                }
                break
        
        # 治疗偏好
        treatment_patterns = [
            (r'吃点药调调|能不能开药', '倾向药物调理'),
            (r'不想做(肠镜|胃镜)|遭罪', '拒绝有创检查'),
            (r'先看看|观察一下', '倾向先观察随访')
        ]
        for pattern, desc in treatment_patterns:
            if re.search(pattern, combined_text):
                extracted_info["治疗偏好"] = {
                    "value": desc + f"（证据：{re.search(pattern, combined_text).group(0)}）",
                    "source": "对话历史",
                    "confidence": 0.9
                }
                break
        
        # 情绪状态
        emotion_patterns = [
            (r'担心|害怕|问题大吗', '担忧病情'),
            (r'遭罪|不舒服|难受', '对检查/症状感到不适'),
            (r'纠结|不知道该不该', '犹豫检查决策')
        ]
        for pattern, desc in emotion_patterns:
            if re.search(pattern, combined_text):
                extracted_info["情绪状态"] = {
                    "value": desc,
                    "source": "对话历史",
                    "confidence": 0.8
                }
                break
        
        # 既往史补充（如未在病历中记录）
        if re.search(r'以前有(高血压|糖尿病|胃病)', combined_text):
            extracted_info["既往史补充"] = {
                "value": re.search(r'以前有(高血压|糖尿病|胃病)', combined_text).group(0),
                "source": "对话历史",
                "confidence": 0.9
            }
        
        return extracted_info
    
    def _llm_supplement(self, patient_speeches: List[str], rule_extracted_info: Dict[str, Dict[str, Any]], medical_record: Dict[str, Any]) -> Dict[str, Dict[str, Any]]:
        """
        LLM补充阶段：使用LLM处理模糊、口语化信息，并结合门诊病历进行补充式提取
        
        Args:
            patient_speeches: 患者发言列表
            rule_extracted_info: 规则抽取的信息
            medical_record: 门诊病历信息
            
        Returns:
            LLM补充的信息
        """
        supplemented_info = {}
        combined_text = " ".join(patient_speeches)
        
        try:
            # 预处理门诊病历信息，提取关键字段（根据实际病历结构）
            medical_record_summary = {}
            # 根据实际病历结构定义所有可能的字段
            key_fields = ["主诉", "现病史", "流行病学史", "既往史", "药物过敏史", 
                          "体格检查", "辅助检查", "处理", "注意事项", "患者去向", "备注"]
            for field in key_fields:
                if field in medical_record:
                    # 简化病历字段值（避免过长）
                    if isinstance(medical_record[field], dict) and "value" in medical_record[field]:
                        medical_record_summary[field] = medical_record[field]["value"]
                    else:
                        medical_record_summary[field] = str(medical_record[field])[:100]  # 截断长文本
            
            # 构建包含病历参考的提示词
            prompt_content = f"""
任务：从患者对话中提取结构化信息，补充门诊病历和规则抽取的遗漏内容。
要求：
1. 【维度定义】严格按以下6个独立维度提取，每个维度必须输出（无信息则填"无"）：
   - 情绪状态：患者对症状/检查/病情的主观感受（如焦虑、抵触、担忧），需关联对话证据；
   - 症状严重程度：症状的频率（偶尔/频繁）、强度（轻微/严重）、当前状态（有无症状），需关联对话证据；
   - 生活影响：症状/检查对日常生活的干扰（如影响饮食、抵触检查），需关联对话证据；
   - 检查史：既往检查类型、时间、结果（优先补充病历未记录的信息），需关联对话证据；
   - 治疗偏好：患者对治疗方式的倾向（如药物/检查/观察），需关联对话证据；
   - 其他关键信息：年龄、家族史等未覆盖的医疗信息，需关联对话证据。

2. 【病历参考规则】：
   - 若对话信息补充了病历缺失的内容（如病历未提检查史，对话提"5年了"），标注"补充自对话"；
   - 若对话与病历冲突（如病历"要求做肠镜"，对话"肠镜不做"），以对话为准，标注"与病历冲突，以对话为准"；
   - 若对话与病历一致（如病历"无过敏史"，对话"没过敏"），标注"与病历一致"，提高置信度。

3. 【证据要求】每个维度的"value"必须包含"具体值+（证据：'患者原句'）"，无证据不提取。

4. 【输出格式】严格JSON，字段名与上述6个维度完全一致，confidence按证据明确度打分（0.6-1.0）。

患者对话内容：{combined_text}

门诊病历参考（关键字段）：
{json.dumps(medical_record_summary, ensure_ascii=False, indent=2)}

已通过规则抽取的信息：
{json.dumps(rule_extracted_info, ensure_ascii=False, indent=2)}

输出示例：
{{
  "情绪状态": {{"value": "对检查流程感到不适（证据：'现在就觉着很遭罪'），与病历无冲突", "confidence": 0.9}},
  "症状严重程度": {{"value": "频率：偶尔，强度：轻微（证据：'偶尔偶尔'），与病历'无明显症状'一致", "confidence": 0.9}},
  "生活影响": {{"value": "抵触肠镜检查（证据：'肠镜不做'），与病历'要求检查肠胃镜'冲突，以对话为准", "confidence": 0.85}},
  "检查史": {{"value": "上次肠胃镜检查距今5年（证据：'5年了'），补充自对话（病历未提及）", "confidence": 0.95}},
  "治疗偏好": {{"value": "倾向药物调理（证据：'有没有可能吃点药调调呢'），补充自对话", "confidence": 0.9}},
  "其他关键信息": {{"value": "年龄60岁（证据：'60了'），补充自对话", "confidence": 1.0}}
}}
            """
            
            # 使用统一的messages格式，与模块三、四保持一致
            model_input = [
                {"role": "system", "content": "你是一位专业的医疗助手，负责从患者对话中提取结构化信息。"},
                {"role": "user", "content": prompt_content}
            ]
            logger.info(f"LLM补充提示词：{prompt_content}")
            # 如果有model_handler属性且可用，调用LLM
            if hasattr(self, 'model_handler') and self.model_handler:
                response = self.model_handler.generate_with_messages(model_input)
                
                # 解析响应
                try:
                    llm_result = json.loads(response)
                    # 为每个补充的信息添加source字段
                    for key, value in llm_result.items():
                        if isinstance(value, dict):
                            supplemented_info[key] = {
                                "value": value.get("value"),
                                "source": "LLM补充",
                                "confidence": value.get("confidence", 0.7)
                            }
                except json.JSONDecodeError:
                    logger.warning("LLM响应解析失败，回退到规则补充")
                    # 回退到规则补充
                    supplemented_info = self._rule_based_supplement(combined_text, rule_extracted_info)
            else:
                # 如果没有model_handler，使用规则补充
                supplemented_info = self._rule_based_supplement(combined_text, rule_extracted_info)
        
        except Exception as e:
            logger.error(f"LLM补充阶段失败: {str(e)}")
            # 发生错误时回退到规则补充
            supplemented_info = self._rule_based_supplement(combined_text, rule_extracted_info)
        
        return supplemented_info
    
    def _rule_based_supplement(self, combined_text: str, rule_extracted_info: Dict[str, Dict[str, Any]]) -> Dict[str, Dict[str, Any]]:
        """
        规则补充：作为LLM补充的回退方案
        
        Args:
            combined_text: 患者对话内容
            rule_extracted_info: 规则抽取的信息
            
        Returns:
            规则补充的信息
        """
        supplemented_info = {}
        
        # 推断情绪状态
        if "情绪状态" not in rule_extracted_info:
            if any(emotion in combined_text for emotion in ["难受", "不舒服", "痛苦", "疼得厉害"]):
                supplemented_info["情绪状态"] = {
                    "value": "难受",
                    "source": "规则补充",
                    "confidence": 0.85
                }
            elif any(emotion in combined_text for emotion in ["还行", "不太疼", "能忍受"]):
                supplemented_info["情绪状态"] = {
                    "value": "尚可忍受",
                    "source": "规则补充",
                    "confidence": 0.85
                }
        
        # 推断症状严重程度
        if "严重程度" not in rule_extracted_info:
            if any(word in combined_text for word in ["很疼", "非常疼", "疼得厉害", "受不了"]):
                supplemented_info["严重程度"] = {
                    "value": "严重",
                    "source": "规则补充",
                    "confidence": 0.8
                }
            elif any(word in combined_text for word in ["有点疼", "轻微疼", "不太疼"]):
                supplemented_info["严重程度"] = {
                    "value": "轻微",
                    "source": "规则补充",
                    "confidence": 0.8
                }
        
        # 推断对日常生活的影响
        if "生活影响" not in rule_extracted_info:
            if any(word in combined_text for word in ["影响工作", "睡不着", "吃不下", "无法正常"]):
                supplemented_info["生活影响"] = {
                    "value": "影响日常生活",
                    "source": "规则补充",
                    "confidence": 0.75
                }
        
        return supplemented_info
    
    def _consistency_check(self, extracted_info: Dict[str, Dict[str, Any]], patient_state: Dict[str, Any],
                       rule_extracted_info: Dict[str, Dict[str, Any]], llm_supplemented_info: Dict[str, Dict[str, Any]]) -> Dict[str, Dict[str, Any]]:
        """
        一致性校验阶段：同时支持规则与LLM交叉校验、提取信息与病历校验
        
        Args:
            extracted_info: 合并后的提取信息
            patient_state: 当前患者状态
            rule_extracted_info: 规则抽取的原始信息
            llm_supplemented_info: LLM补充的原始信息
            
        Returns:
            经过多源校验后的信息（含置信度调整和一致性状态标注）
        """
        validated_info = extracted_info.copy()
        patient_state_data = patient_state.get("PatientState", {})
        
        try:
            # --------------------------
            # 1. 规则抽取与LLM补充的交叉校验（新增核心逻辑）
            # --------------------------
            cross_validation = {}  # 临时存储跨源校验结果
            for key in validated_info:
                rule_has = key in rule_extracted_info
                llm_has = key in llm_supplemented_info
                
                if rule_has and llm_has:
                    # 两者均提取该字段：检查内容一致性
                    rule_val = rule_extracted_info[key]["value"].lower()
                    llm_val = llm_supplemented_info[key]["value"].lower()
                    
                    # 宽松匹配（允许同义或部分重合）
                    if (rule_val in llm_val) or (llm_val in rule_val) or self._is_synonym(rule_val, llm_val):
                        # 内容一致：提升置信度
                        validated_info[key]["confidence"] = min(validated_info[key]["confidence"] + 0.15, 1.0)
                        cross_validation[key] = "规则与LLM提取一致，可信度提升"
                    else:
                        # 内容冲突：取高置信度来源，轻微降置信度
                        rule_conf = rule_extracted_info[key]["confidence"]
                        llm_conf = llm_supplemented_info[key]["confidence"]
                        
                        if rule_conf >= llm_conf:
                            validated_info[key]["confidence"] = max(validated_info[key]["confidence"] - 0.05, 0.0)
                            cross_validation[key] = f"规则与LLM冲突，以规则为准（规则置信度{rule_conf}）"
                        else:
                            validated_info[key]["confidence"] = max(validated_info[key]["confidence"] - 0.05, 0.0)
                            cross_validation[key] = f"规则与LLM冲突，以LLM为准（LLM置信度{llm_conf}）"
                elif rule_has:
                    cross_validation[key] = "仅规则提取，无LLM印证"
                elif llm_has:
                    cross_validation[key] = "仅LLM提取，无规则印证"
                else:
                    cross_validation[key] = "来源不明"
            
            # --------------------------
            # 2. 与病历信息的一致性校验（保留完整逻辑）
            # --------------------------
            chief_complaint = patient_state_data.get("主诉", {}).get("value", "").lower()
            present_illness = patient_state_data.get("现病史", {})
            past_history = patient_state_data.get("既往史", {}).get("value", "").lower()
            auxiliary_exam = patient_state_data.get("辅助检查", {}).get("结果", {}).get("value", "").lower()
            treatment_plan = patient_state_data.get("处理与建议", {}).get("处理", {}).get("value", "").lower()
            
            # 2.1 疼痛部位校验
            if "疼痛部位" in validated_info:
                pain_loc = validated_info["疼痛部位"]["value"].lower()
                record_check = []
                # 与主诉校验
                if chief_complaint and (pain_loc in chief_complaint or self._is_synonym(pain_loc, chief_complaint)):
                    validated_info["疼痛部位"]["confidence"] = min(validated_info["疼痛部位"]["confidence"] + 0.08, 1.0)
                    record_check.append(f"与主诉一致（匹配：{pain_loc}）")
                # 与现病史冲突校验
                if present_illness and "症状" in present_illness:
                    illness_symptom = present_illness["症状"].get("value", "").lower()
                    if "无" in illness_symptom and (pain_loc in illness_symptom or self._is_synonym(pain_loc, illness_symptom)):
                        validated_info["疼痛部位"]["confidence"] = max(validated_info["疼痛部位"]["confidence"] - 0.35, 0.0)
                        record_check.append(f"与现病史冲突（现病史：{illness_symptom}）")
                validated_info["疼痛部位"]["病历校验"] = "; ".join(record_check) if record_check else "未匹配主诉/现病史"
            
            # 2.2 疼痛性质校验
            if "疼痛性质" in validated_info:
                pain_nature = validated_info["疼痛性质"]["value"].lower()
                record_check = []
                # 与现病史关联校验
                if present_illness and "症状" in present_illness:
                    illness_symptom = present_illness["症状"].get("value", "").lower()
                    if any(pain_word in illness_symptom for pain_word in ["疼", "痛", "不适"]):
                        validated_info["疼痛性质"]["confidence"] = min(validated_info["疼痛性质"]["confidence"] + 0.05, 1.0)
                        record_check.append("与现病史症状关联一致")
                # 与辅助检查冲突校验
                if auxiliary_exam and "无明显异常" in auxiliary_exam and "剧烈" in pain_nature:
                    validated_info["疼痛性质"]["confidence"] = max(validated_info["疼痛性质"]["confidence"] - 0.25, 0.0)
                    record_check.append("与辅助检查轻微冲突（检查无明显异常）")
                validated_info["疼痛性质"]["病历校验"] = "; ".join(record_check) if record_check else "未匹配现病史/辅助检查"
            
            # 2.3 检查史校验
            if "检查史" in validated_info:
                checkup_info = validated_info["检查史"]["value"].lower()
                record_check = []
                # 与既往史一致性校验
                if past_history and any(exam in past_history for exam in ["胃镜", "肠镜", "活检"]):
                    validated_info["检查史"]["confidence"] = min(validated_info["检查史"]["confidence"] + 0.1, 1.0)
                    record_check.append("与既往史检查记录一致")
                # 与辅助检查补充校验
                if auxiliary_exam and "无" in auxiliary_exam and any(exam in checkup_info for exam in ["胃镜", "肠镜"]):
                    validated_info["检查史"]["confidence"] = min(validated_info["检查史"]["confidence"] + 0.05, 1.0)
                    record_check.append("补充辅助检查未记录的历史检查")
                validated_info["检查史"]["病历校验"] = "; ".join(record_check) if record_check else "未匹配既往史/辅助检查"
            
            # 2.4 治疗偏好校验
            if "治疗偏好" in validated_info:
                treatment_pref = validated_info["治疗偏好"]["value"].lower()
                record_check = []
                # 与处理建议冲突/一致校验
                if treatment_plan and "肠镜" in treatment_plan and "拒绝有创检查" in treatment_pref:
                    validated_info["治疗偏好"]["confidence"] = min(validated_info["治疗偏好"]["confidence"] + 0.05, 1.0)
                    record_check.append("与处理建议冲突（以患者表述为准）")
                if treatment_plan and "药物" in treatment_plan and "倾向药物调理" in treatment_pref:
                    validated_info["治疗偏好"]["confidence"] = min(validated_info["治疗偏好"]["confidence"] + 0.1, 1.0)
                    record_check.append("与处理建议一致")
                validated_info["治疗偏好"]["病历校验"] = "; ".join(record_check) if record_check else "未匹配处理建议"
            
            # 2.5 既往史补充校验
            if "既往史补充" in validated_info:
                past_supplement = validated_info["既往史补充"]["value"].lower()
                record_check = []
                if past_history and (past_supplement in past_history or any(disease in past_history for disease in ["高血压", "糖尿病", "胃病"])):
                    validated_info["既往史补充"]["confidence"] = min(validated_info["既往史补充"]["confidence"] + 0.08, 1.0)
                    record_check.append("与既往史记录一致")
                if past_history and "无特殊" in past_history and any(disease in past_supplement for disease in ["高血压", "糖尿病", "胃病"]):
                    validated_info["既往史补充"]["confidence"] = min(validated_info["既往史补充"]["confidence"] + 0.1, 1.0)
                    record_check.append("补充既往史未记录的疾病信息")
                validated_info["既往史补充"]["病历校验"] = "; ".join(record_check) if record_check else "未匹配既往史"
            
            # --------------------------
            # 3. 整合校验结果（跨源+病历）
            # --------------------------
            for key in validated_info:
                # 处理未单独校验的字段（如疼痛频率、其他症状等）
                if "病历校验" not in validated_info[key]:
                    if any([chief_complaint, present_illness, past_history]):
                        validated_info[key]["病历校验"] = "未发现冲突（无直接匹配的病历信息）"
                    else:
                        validated_info[key]["病历校验"] = "无病历信息可参考"
                
                # 合并跨源校验与病历校验状态
                validated_info[key]["一致性状态"] = f"[跨源校验] {cross_validation.get(key, '未校验')}; [病历校验] {validated_info[key]['病历校验']}"
                
                # 移除临时键
                del validated_info[key]["病历校验"]
        
        except Exception as e:
            logger.warning(f"一致性校验失败: {str(e)}")
            for key in validated_info:
                validated_info[key]["一致性状态"] = f"校验失败: {str(e)}"
    
        return validated_info
    
    def _is_synonym(self, term1: str, term2: str) -> bool:
        """
        简单判断两个术语是否为同义词
        
        Args:
            term1: 第一个术语
            term2: 第二个术语
            
        Returns:
            是否为同义词
        """
        # 简单同义词映射表
        synonyms = {
            "肚子": ["腹部", "胃"],
            "腹部": ["肚子", "胃"],
            "胃": ["肚子", "腹部"],
            "心口": ["胸部", "心脏部位"],
            "胸部": ["心口", "心脏部位"]
        }
        
        # 检查term1的同义词是否在term2中
        if term1 in synonyms:
            for synonym in synonyms[term1]:
                if synonym in term2:
                    return True
        
        # 检查term2的同义词是否在term1中
        if term2 in synonyms:
            for synonym in synonyms[term2]:
                if synonym in term1:
                    return True
        
        return False
    
    def _parse_string_medical_record(self, medical_text: str) -> Dict[str, Any]:
        """
        解析字符串格式的病历
        
        Args:
            medical_text: 病历文本
            
        Returns:
            解析后的病历信息，确保所有病历字段都是同级关系
        """
        result = {}
        
        # 简单的文本解析逻辑，支持多行内容收集
        lines = medical_text.split('\n')
        current_section = None
        section_contents = {}
        
        # 定义所有可能的病历章节
        all_sections = ['主诉', '现病史', '既往史', '药物过敏史', '流行病学史', 
                       '体格检查', '辅助检查', '处理', '注意事项', '患者去向', '备注']
        
        for line in lines:
            line = line.strip()
            if not line:
                continue
                
            # 检查是否是新的章节标题
            new_section_found = False
            for section in all_sections:
                if section in line:
                    current_section = section
                    # 提取章节内容，去除标题部分
                    value = line.split('：', 1)[-1] if '：' in line else line.split(':', 1)[-1]
                    section_contents[section] = value.strip()
                    new_section_found = True
                    break
            
            # 如果不是新章节，且当前有活跃章节，则继续收集内容
            if not new_section_found and current_section and line:
                if current_section in section_contents:
                    section_contents[current_section] += ' ' + line
        
        # 处理收集到的所有内容，确保所有字段都是同级关系
        for section, content in section_contents.items():
            if section == '主诉':
                # 主诉保持纯净，不包含任何现病史信息
                result[section] = {
                    "value": content,
                    "source": "门诊病历",
                    "confidence": 1.0
                }
            elif section == '现病史':
                # 现病史单独处理，使用专门的解析方法处理完整内容
                result[section] = self._extract_illness_from_text(content)
            elif section == '体格检查':
                result[section] = {
                    "阳性体征": {
                        "value": content if content else "无专科阳性特征",
                        "source": "病历",
                        "confidence": 0.9
                    }
                }
            elif section == '辅助检查':
                result[section] = {
                    "结果": {
                        "value": content if content else "无",
                        "source": "病历",
                        "confidence": 1.0
                    }
                }
            else:
                # 其他所有字段都保持同级关系
                result[section] = {
                    "value": content,
                    "source": "门诊病历",
                    "confidence": 1.0
                }
        
        # 特殊处理：如果存在"处理"、"注意事项"、"患者去向"这些字段，同时也创建"处理与建议"结构
        # 但不改变原字段的同级关系
        if any(key in result for key in ["处理", "注意事项", "患者去向"]):
            result["处理与建议"] = {}
            for key in ["处理", "注意事项", "患者去向"]:
                if key in result:
                    result["处理与建议"][key] = result[key]
        
        # 如果解析结果为空，使用默认值
        if not result:
            result = self._get_default_medical_record()
        
        return result
    
    def _get_default_medical_record(self) -> Dict[str, Any]:
        """
        获取默认的病历信息
        
        Returns:
            默认病历信息
        """
        return {
            "主诉": {"value": "未知", "source": "缺失", "confidence": 0.0},
            "现病史": {
                "起病时间": {"value": "未知", "source": "缺失", "confidence": 0.0},
                "症状": {"value": "未知", "source": "缺失", "confidence": 0.0},
                "诱因": {"value": "未知", "source": "缺失", "confidence": 0.0},
                "伴随症状": {"value": None, "source": "缺失", "confidence": 0.0},
                "进展": {"value": "未知", "source": "缺失", "confidence": 0.0}
            },
            "既往史": {"value": "未知", "source": "缺失", "confidence": 0.0},
            "药物过敏史": {"value": "无", "source": "缺失", "confidence": 0.0},
            "流行病学史": {"value": "无", "source": "缺失", "confidence": 0.0},
            "体格检查": {
                "阳性体征": {"value": "无专科阳性特征", "source": "缺失", "confidence": 0.0}
            },
            "辅助检查": {
                "结果": {"value": "无", "source": "缺失", "confidence": 0.0}
            },
            "处理与建议": {
                "处理": {"value": "无", "source": "缺失", "confidence": 0.0},
                "注意事项": {"value": "无", "source": "缺失", "confidence": 0.0},
                "患者去向": {"value": "未知", "source": "缺失", "confidence": 0.0}
            }
        }
    
    def _calculate_completeness(self, basic_info: Dict, medical_record: Dict, dialogue_info: Dict) -> float:
        """
        计算信息完整度
        
        Args:
            basic_info: 基本信息
            medical_record: 病历信息
            dialogue_info: 对话信息
            
        Returns:
            完整度分数 (0-1)
        """
        try:
            total_fields = 0
            filled_fields = 0
            
            # 统计基本信息完整度
            for field_info in basic_info.values():
                total_fields += 1
                if field_info.get("value") and field_info.get("value") != "未知":
                    filled_fields += 1
            
            # 统计病历信息完整度
            for key, value in medical_record.items():
                if isinstance(value, dict):
                    if "value" in value:
                        total_fields += 1
                        if value.get("value") and value.get("value") != "未知" and value.get("value") != "无":
                            filled_fields += 1
                    else:
                        # 嵌套结构
                        for sub_key, sub_value in value.items():
                            if isinstance(sub_value, dict) and "value" in sub_value:
                                total_fields += 1
                                if sub_value.get("value") and sub_value.get("value") != "未知" and sub_value.get("value") != "无":
                                    filled_fields += 1
            
            # 统计对话信息完整度
            if "主观表述" in dialogue_info:
                for field_info in dialogue_info["主观表述"].values():
                    total_fields += 1
                    if field_info.get("value") and field_info.get("value") != "未知":
                        filled_fields += 1
            
            completeness = filled_fields / total_fields if total_fields > 0 else 0.0
            return round(completeness, 2)
            
        except Exception as e:
            logger.warning(f"计算信息完整度失败: {str(e)}")
            return 0.0
    
    def _get_empty_state(self) -> Dict[str, Any]:
        """
        获取空的患者状态
        
        Returns:
            空状态结构
        """
        return {
            "PatientState": {
                "基本信息": {
                    "姓名": {"value": "未知", "source": "错误", "confidence": 0.0},
                    "性别": {"value": "未知", "source": "错误", "confidence": 0.0},
                    "年龄": {"value": "未知", "source": "错误", "confidence": 0.0}
                },
                "系统状态": {
                    "更新时间": self.current_time,
                    "对话轮次": 0,
                    "信息完整度": 0.0
                }
            }
        }
